/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.solr.handler.component;

import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.search.Query;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.HighlightParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.SimpleOrderedMap;
import org.apache.solr.highlight.SolrHighlighter;
import org.apache.solr.highlight.DefaultSolrHighlighter;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.search.QParser;
import org.apache.solr.util.plugin.PluginInfoInitialized;
import org.apache.solr.util.plugin.SolrCoreAware;
import org.apache.solr.core.PluginInfo;
import org.apache.solr.core.SolrCore;

import java.io.IOException;
import java.net.URL;
import java.util.Map;
import java.util.List;

/**
 * TODO!
 * 
 * @version $Id: HighlightComponent.java 1198785 2011-11-07 15:43:23Z koji $
 * @since solr 1.3
 */
public class HighlightComponent extends SearchComponent implements
		PluginInfoInitialized, SolrCoreAware {
	public static final String COMPONENT_NAME = "highlight";
	private PluginInfo info = PluginInfo.EMPTY_INFO;
	private SolrHighlighter highlighter;

	public void init(PluginInfo info) {
		this.info = info;
	}

	@Override
	public void prepare(ResponseBuilder rb) throws IOException {
		SolrParams params = rb.req.getParams();
		rb.doHighlights = highlighter.isHighlightingEnabled(params);
		if (rb.doHighlights) {
			String hlq = params.get(HighlightParams.Q);
			if (hlq != null) {
				try {
					QParser parser = QParser.getParser(hlq, null, rb.req);
					rb.setHighlightQuery(parser.getHighlightQuery());
				} catch (ParseException e) {
					throw new SolrException(
							SolrException.ErrorCode.BAD_REQUEST, e);
				}
			}
		}
	}

	public void inform(SolrCore core) {
		List<PluginInfo> children = info.getChildren("highlighting");
		if (children.isEmpty()) {
			PluginInfo pluginInfo = core.getSolrConfig().getPluginInfo(
					SolrHighlighter.class.getName()); // TODO deprecated
														// configuration remove
														// later
			if (pluginInfo != null) {
				highlighter = core.createInitInstance(pluginInfo,
						SolrHighlighter.class, null,
						DefaultSolrHighlighter.class.getName());
				highlighter.initalize(core.getSolrConfig());
			} else {
				DefaultSolrHighlighter defHighlighter = new DefaultSolrHighlighter(
						core);
				defHighlighter.init(PluginInfo.EMPTY_INFO);
				highlighter = defHighlighter;
			}
		} else {
			highlighter = core.createInitInstance(children.get(0),
					SolrHighlighter.class, null,
					DefaultSolrHighlighter.class.getName());
		}

	}

	@Override
	public void process(ResponseBuilder rb) throws IOException {
		if (rb.doHighlights) {
			SolrQueryRequest req = rb.req;
			SolrParams params = req.getParams();

			String[] defaultHighlightFields; // TODO: get from builder by
												// default?

			if (rb.getQparser() != null) {
				defaultHighlightFields = rb.getQparser()
						.getDefaultHighlightFields();
			} else {
				defaultHighlightFields = params.getParams(CommonParams.DF);
			}

			Query highlightQuery = rb.getHighlightQuery();
			if (highlightQuery == null) {
				if (rb.getQparser() != null) {
					try {
						highlightQuery = rb.getQparser().getHighlightQuery();
						rb.setHighlightQuery(highlightQuery);
					} catch (Exception e) {
						throw new SolrException(
								SolrException.ErrorCode.BAD_REQUEST, e);
					}
				} else {
					highlightQuery = rb.getQuery();
					rb.setHighlightQuery(highlightQuery);
				}
			}

			if (highlightQuery != null) {
				boolean rewrite = !(Boolean.valueOf(params.get(
						HighlightParams.USE_PHRASE_HIGHLIGHTER, "true")) && Boolean
						.valueOf(params.get(
								HighlightParams.HIGHLIGHT_MULTI_TERM, "true")));
				highlightQuery = rewrite ? highlightQuery.rewrite(req
						.getSearcher().getReader()) : highlightQuery;
			}

			// No highlighting if there is no query -- consider q.alt="*:*
			if (highlightQuery != null) {
				NamedList sumData = highlighter.doHighlighting(
						rb.getResults().docList, highlightQuery, req,
						defaultHighlightFields);

				if (sumData != null) {
					// TODO ???? add this directly to the response?
					rb.rsp.add("highlighting", sumData);
				}
			}
		}
	}

	@Override
	public void modifyRequest(ResponseBuilder rb, SearchComponent who,
			ShardRequest sreq) {
		if (!rb.doHighlights)
			return;

		// Turn on highlighting only only when retrieving fields
		if ((sreq.purpose & ShardRequest.PURPOSE_GET_FIELDS) != 0) {
			sreq.purpose |= ShardRequest.PURPOSE_GET_HIGHLIGHTS;
			// should already be true...
			sreq.params.set(HighlightParams.HIGHLIGHT, "true");
		} else {
			sreq.params.set(HighlightParams.HIGHLIGHT, "false");
		}
	}

	@Override
	public void handleResponses(ResponseBuilder rb, ShardRequest sreq) {
	}

	@Override
	public void finishStage(ResponseBuilder rb) {
		if (rb.doHighlights && rb.stage == ResponseBuilder.STAGE_GET_FIELDS) {

			Map.Entry<String, Object>[] arr = new NamedList.NamedListEntry[rb.resultIds
					.size()];

			// TODO: make a generic routine to do automatic merging of id keyed
			// data
			for (ShardRequest sreq : rb.finished) {
				if ((sreq.purpose & ShardRequest.PURPOSE_GET_HIGHLIGHTS) == 0)
					continue;
				for (ShardResponse srsp : sreq.responses) {
					NamedList hl = (NamedList) srsp.getSolrResponse()
							.getResponse().get("highlighting");
					for (int i = 0; i < hl.size(); i++) {
						String id = hl.getName(i);
						ShardDoc sdoc = rb.resultIds.get(id);
						int idx = sdoc.positionInResponse;
						arr[idx] = new NamedList.NamedListEntry<Object>(id,
								hl.getVal(i));
					}
				}
			}

			// remove nulls in case not all docs were able to be retrieved
			rb.rsp.add("highlighting", removeNulls(new SimpleOrderedMap(arr)));
		}
	}

	static NamedList removeNulls(NamedList nl) {
		for (int i = 0; i < nl.size(); i++) {
			if (nl.getName(i) == null) {
				NamedList newList = nl instanceof SimpleOrderedMap ? new SimpleOrderedMap()
						: new NamedList();
				for (int j = 0; j < nl.size(); j++) {
					String n = nl.getName(j);
					if (n != null) {
						newList.add(n, nl.getVal(j));
					}
				}
				return newList;
			}
		}
		return nl;
	}

	public SolrHighlighter getHighlighter() {
		return highlighter;
	}

	// //////////////////////////////////////////
	// / SolrInfoMBean
	// //////////////////////////////////////////

	@Override
	public String getDescription() {
		return "Highlighting";
	}

	@Override
	public String getVersion() {
		return "$Revision: 1198785 $";
	}

	@Override
	public String getSourceId() {
		return "$Id: HighlightComponent.java 1198785 2011-11-07 15:43:23Z koji $";
	}

	@Override
	public String getSource() {
		return "$URL: https://svn.apache.org/repos/asf/lucene/dev/branches/lucene_solr_3_5/solr/core/src/java/org/apache/solr/handler/component/HighlightComponent.java $";
	}

	@Override
	public URL[] getDocs() {
		return null;
	}
}
